package com.sleepingrain;

import java.util.Random;

/**
 * GenFire implements the fire generator logic. It consists of multiple
 * generators responsible for individual sounds: <br>
 * FireGenerator - background sound <br>
 * HissingGenerator - hissing sound <br>
 * CracklingGenerator - crackling sound <br>
 * CricketGenerator - crickets sound <br>
 * 
 */

/**
 * FireGenerator generates background sound.
 */
class FireGenerator extends SoundGenerator {
    private int m_time;
    private Random m_rnd;
    private int m_volume = 10;
    private int m_chunk_size;

    FireGenerator(SoundBuffer sb, SampleLib lib) {
	super(sb, lib);
	this.m_rnd = new Random();
	this.m_volume = 0;
	this.m_chunk_size = m_soundBuffer.sampleRate() / 10;
    }

    public void config_setVolume(int progress) {
	this.beginConfig();
	this.m_volume = Math.max(Math.min(progress, 100), 1);
	this.endConfig();
    }

    private float angle = 0;

    private int volumeChanger(int vol, int chunkSize) {
	float increment = (float) (((2 * Math.PI) * this.m_rnd.nextInt(10) * chunkSize) / 44100);

	int result = vol + (int) ((vol / 10) * ((Math.sin(angle) - 1) / 2));
	angle += increment;
	return result;
    }

    @Override
    protected void generate() {
	Sample s = this.m_sampleLib.get(0);
	if (s != null) {
	    int offSet = 0;
	    int delaySamples = (m_soundBuffer.sampleRate() * (20 / 33000));
	    int sampleCount = 0;
	    int volume = 0;

	    while (s.sampleCount - offSet > 0) {
		volume = volumeChanger(this.m_volume, this.m_chunk_size);
		sampleCount = Math.min(this.m_chunk_size, s.sampleCount
			- offSet);
		// m_time = m_soundBuffer.insertSampleEx(this, 0,m_time,
		// s,offSet,sampleCount, m_volume, m_newVolume);
		// m_time = m_soundBuffer.insertSampleEx(this, 1,m_time,
		// s,offSet,sampleCount, m_volume, m_newVolume);
		// set sample to the left channel
		m_time = m_soundBuffer.insertSample(this, 0, m_time, s, offSet,
			sampleCount, volume);
		// set sample to the right channel with delay
		m_time = m_soundBuffer.insertSample(this, 1, m_time
			+ delaySamples, s, offSet, sampleCount, volume);
		// increase offSet to read next chunk
		offSet += sampleCount;
		m_time += sampleCount;
	    }
	}
    }
}

/**
 * HissingGenerator generates hissing sound.
 * 
 */
class HissingGenerator extends SoundGenerator {

    private int m_time;
    private Random m_rnd;
    private int m_intensity = 30;
    private int m_volume = 40;

    HissingGenerator(SoundBuffer sb, SampleLib lib) {
	super(sb, lib);
	this.m_rnd = new Random();
	this.m_time = 0;
	// m_soundBuffer.divisor = 100;
    }

    public void config_setIntensity(int progress) {
	this.beginConfig();
	this.m_intensity = Math.max(Math.min(progress, 100), 1);
	this.endConfig();
    }

    public void config_setVolume(int progress) {
	this.beginConfig();
	this.m_volume = progress;
	this.endConfig();
    }

    @Override
    protected void generate() {
	{
	    Sample s = this.m_sampleLib.get(this.m_rnd.nextInt(this.m_sampleLib
		    .size()));

	    if (s != null) {
		int delaySamples = (m_soundBuffer.sampleRate() * (20 / 33000));
		int volume = this.m_volume;
		if (this.m_volume > 2) {
		    // set random volume
		    volume += (int) ((volume / 1.2) * ((this.m_rnd
			    .nextGaussian() - 1) / 2));
		}

		m_time = m_soundBuffer.insertSample(this, 0, m_time, s, 0,
			s.sampleCount, volume);
		m_time = m_soundBuffer.insertSample(this, 1, m_time
			+ delaySamples, s, 0, s.sampleCount, volume);
		m_time += m_rnd.nextInt(4000 + 5000 / this.m_intensity);
	    }
	}
    }
}

/**
 * CracklingGenerator - crackling sound.
 * 
 */
class CracklingGenerator extends SoundGenerator {

    private int m_time;
    private Random m_rnd;
    private int m_intensity = 20;
    private int m_volume = 30;

    CracklingGenerator(SoundBuffer sb, SampleLib lib) {
	super(sb, lib);
	this.m_rnd = new Random();
	this.m_time = 0;
	// m_soundBuffer.divisor = 100;
    }

    public void config_setIntensity(int progress) {
	this.beginConfig();
	this.m_intensity = Math.max(Math.min(progress, 100), 1);
	this.endConfig();
    }

    public void config_setVolume(int progress) {
	this.beginConfig();
	this.m_volume = Math.max(Math.min(progress, 100), 0);
	this.endConfig();
    }

    @Override
    protected void generate() {
	{
	    Sample s = this.m_sampleLib.get(this.m_rnd.nextInt(this.m_sampleLib
		    .size()));
	    if (s != null) {
		int delaySamples = (m_soundBuffer.sampleRate() * (20 / 33000));
		int volume = this.m_volume;
		if (this.m_volume > 2) {
		    // set random volume
		    volume += (int) ((volume / 5) * (-this.m_rnd.nextFloat()));
		}
		m_time = m_soundBuffer.insertSample(this, 0, m_time, s, 0,
			s.sampleCount, volume);
		m_time = m_soundBuffer.insertSample(this, 1, m_time
			+ delaySamples, s, 0, s.sampleCount, volume);
		this.m_time += (5000 + this.m_rnd
			.nextInt(30000 + (1000000 / this.m_intensity)));
	    }
	}
    }
}

/**
 * CricketGenerator - crickets sound.
 * 
 */
class CricketGenerator extends SoundGenerator {

    private int m_time;
    private Random m_rnd;
    private int m_intensity = 20;
    private int m_volume = 30;

    CricketGenerator(SoundBuffer sb, SampleLib lib) {
	super(sb, lib);
	this.m_rnd = new Random();
	this.m_time = 0;
    }

    public void config_setIntensity(int progress) {
	this.beginConfig();
	this.m_intensity = Math.max(Math.min(progress, 100), 1);
	this.endConfig();
    }

    public void config_setVolume(int progress) {
	this.beginConfig();
	this.m_volume = Math.max(Math.min(progress, 100), 0);
	this.endConfig();
    }

    @Override
    protected void generate() {
	{
	    Sample s = this.m_sampleLib.get(this.m_rnd.nextInt(this.m_sampleLib
		    .size()));
	    if (s != null) {
		int delaySamples = (m_soundBuffer.sampleRate() * (20 / 33000));
		int volume = this.m_volume;
		if (this.m_volume > 1) {
		    // set random volume
		    volume += (int) ((volume / 2) * (-this.m_rnd.nextFloat()));
		    volume /= 10;
		}
		this.m_time = m_soundBuffer.insertSample(this, 0, m_time, s, 0,
			s.sampleCount, volume);
		this.m_time = m_soundBuffer.insertSample(this, 1, m_time
			+ delaySamples, s, 0, s.sampleCount, volume);
		this.m_time += (int) (s.sampleCount * (1.0f / Math.max(1,
			this.m_rnd.nextInt(m_intensity))));
	    }
	}
    }
}
